package com.christianposta.learnddd.domain.model.voyage;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.Validate;

import com.christianposta.learnddd.domain.model.location.Location;
import com.christianposta.learnddd.domain.shared.Entity;

public class Voyage implements Entity<Voyage> {
	
	private VoyageNumber voyageNumber;
	private Schedule schedule;
	
	public static final Voyage NONE = new Voyage(new VoyageNumber(""), Schedule.EMPTY);
	
	/**
	 * A Voyage is an entity object in the domain that needs to be kept track of some
	 * how. That's why it has an identity. When comparing voyages, the voyage id is the
	 * only identity that we need to worry about. 
	 * 
	 * @param voyageNumber
	 * @param schedule
	 */
	public Voyage(final VoyageNumber voyageNumber, final Schedule schedule){
		Validate.notNull(voyageNumber);
		Validate.notNull(schedule);
		
		this.voyageNumber = voyageNumber;
		this.schedule = schedule;
	}
	
	public VoyageNumber voyageNumber(){
		return this.voyageNumber;
	}
	
	public Schedule schedule(){
		return this.schedule;
	}

	public boolean sameIdentityAs(Voyage other) {
		return other != null && this.voyageNumber.sameValueAs(other.voyageNumber);
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj == this) return true;
		if(obj == null || obj.getClass() != getClass()) return false;
		
		Voyage that = (Voyage)obj;
		
		return sameIdentityAs(that);
		
	}
	
	@Override
	public int hashCode() {
		return this.voyageNumber.hashCode();
	}
	
	@Override
	public String toString() {
		return "Voyage: " + this.voyageNumber;
	}

	Voyage() {
		// Needed by Hibernate
	}

	// Needed by Hibernate
	private Long id;
	
	
	/**
	 * Aggregate factory.
	 * Useful to build new Voyages without having to (allowed to?) know about the internal
	 * descriptions/details about how a Voyage is constructed.
	 * 
	 * @author ceposta
	 *
	 */
	public static final class Builder {
		
		private final List<CarrierMovement> carrierMovements = new ArrayList<CarrierMovement>();
		private final VoyageNumber voyageNumber;
		private Location departureLocation;
		
		public Builder(final VoyageNumber voyageNumber, final Location departureLocation){
			Validate.notNull(voyageNumber);
			Validate.notNull(departureLocation);
			
			this.voyageNumber = voyageNumber;
			this.departureLocation = departureLocation;
		}
		
		public Builder addMovement(Location arrivalLocation, Date departureTime, Date arrivalTime){
			this.carrierMovements.add(new CarrierMovement(this.departureLocation, arrivalLocation, departureTime, arrivalTime));
		    
			// Next departure location is the same as this arrival location
			this.departureLocation = arrivalLocation;
			
			return this;
		}
		
		public Voyage build(){
			return new Voyage(this.voyageNumber, new Schedule(this.carrierMovements));
		}
		
	}

}
